package it.uniroma2.dtk.op;

import it.uniroma2.util.vector.VectorComposer;
import it.uniroma2.util.vector.VectorProvider;

import java.util.Arrays;
import java.util.Random;

/**
 * @author Lorenzo Dell'Arciprete
 * Abstract class representing operations that can be split into a transformation and a composition.
 * It provides the permutations to be used by operations that use shuffling as transformation.
 */
public abstract class TransformAndCompose implements IdealOperation {

	protected int[] permutation1;	// Permutation p_1 of shuffled gamma-product and shuffled circular convolution
	protected int[] permutation2;	// Permutation p_2 of shuffled gamma-product and shuffled circular convolution
	
	public abstract double[] op(double[] x, double[] y);

	public void initialize(VectorProvider vp) throws Exception {
		int size = vp.getVectorSize();
		permutation1 = generatePermutation(size, 0, null);
		permutation2 = generatePermutation(size, 1, permutation1);
	}
	
	protected int[] generatePermutation(int size, int seed, int[] anotherPermutation) {
		Random rand = new Random(seed);
		int[] permutation = generateRandomPermutation(size, rand);
		while (!testPermutation(permutation, anotherPermutation)) {
			System.out.println("Discarding permutation!");
			permutation = generateRandomPermutation(size, rand);
		}
		return permutation;
	}
	
	protected int[] generateRandomPermutation(int size, Random rand) {
		int[] permutation = new int[size];
		double[] toOrder = new double[size];;
		for (int i=0; i<size; i++)
			toOrder[i] = rand.nextGaussian();
		double[] ordered = Arrays.copyOf(toOrder, size);
		Arrays.sort(ordered);
		for (int i=0; i<size; i++)
			for (int j=0; j<size; j++)
				if (toOrder[i] == ordered[j]) {
					permutation[i] = toOrder[i]<0 ? -j : j;
					break;
				}
		return permutation;
	}
	
	/**
	 * The permutation is valid if its cycle is long enough, 
	 * and, optionally, if it is not equal or the inverse of another given permutation
	 */
	protected boolean testPermutation(int[] permutation, int[] anotherPermutation) {
		double[] basic = new double[permutation.length];
		for (int i=0; i<basic.length; i++)
			basic[i] = i;
		double[] shuffled = Arrays.copyOf(basic, basic.length);
		for (int i=0; i<basic.length; i++) {
			shuffled = VectorComposer.shuffle(shuffled, permutation);
			if (Arrays.equals(shuffled, basic))
				return false; 
			if (anotherPermutation != null) {
				if (Arrays.equals(shuffled, VectorComposer.shuffle(basic, anotherPermutation)))
					return false;
				if (Arrays.equals(basic, VectorComposer.shuffle(shuffled, anotherPermutation)))
					return false;
			}
		}
		return true;
	}

}
